(function() {
	Ender.Game.Classes.Feat = function(featId, featData) {
		this.id = featId;
		$.extend(this, featData);
	};

	Ender.Game.Classes.Feat.prototype.apply = function(character) {
		if (this.effect.saveBonus) {
			for ( var save in this.effect.saveBonus) {
				character.bonus.save[save] += this.effect.saveBonus[save];
			}
		}
		if (this.effect.skillBonus) {
			for ( var skill in this.effect.skillBonus) {
				if (!character.bonus.skill[skill])
					character.bonus.skill[skill] = 0;
				character.bonus.skill[skill] += this.effect.skillBonus[skill];
			}
		}
		if (this.effect.creatureSize) {
			character.size = Ender.Game.Enums.CreatureSize[this.effect.creatureSize];
		}
		if (this.effect.armorProficiency) {
			character.proficiencies.armor[this.effect.armorProficiency] = true;
		}
		if (this.effect.shieldProficiency) {
			character.proficiencies.shield[this.effect.shieldProficiency] = true;
		}
		if (this.effect.weaponProficiency) {
			for ( var i = 0; i < this.effect.weaponProficiency.length; i++) {
				var weapon = this.effect.weaponProficiency[i];
				character.proficiencies.weapon[weapon] = true;
			}
		}
		if (this.effect.attackBonus) {
			var attackBonus = this.effect.attackBonus;
			if (attackBonus.weaponTypes) {
				character.bonus.attack.push(function(character, enemyCharacter, roll, range) {
					var alteredRoll = roll;

					for ( var i = 0; i < attackBonus.weaponTypes.length; i++) {
						if (character.getEquippedWeapon().isOfType(attackBonus.weaponTypes[i])) {
							alteredRoll += attackBonus.value;
							break;
						}
					}

					return alteredRoll;
				});
			}
		}
		if (this.effect.evasionBonus) {
			var evasionBonus = this.effect.evasionBonus;
			for ( var type in evasionBonus) {
				if (character.bonus.evasion.stackable[type] !== undefined) {
					character.bonus.evasion.stackable[type] += evasionBonus[type];
				} else {
					character.bonus.evasion.unstackable[type] = evasionBonus[type];
				}
			}
		}
		if (this.effect.damageBonus) {
			var damageBonus = this.effect.damageBonus;
			if (damageBonus.weaponTypes) {
				character.bonus.damage.push(function(character, enemyCharacter, roll, range) {
					var alteredRoll = roll;

					for ( var i = 0; i < damageBonus.weaponTypes.length; i++) {
						if (character.getEquippedWeapon().isOfType(damageBonus.weaponTypes[i])) {
							alteredRoll += damageBonus.value;
							break;
						}
					}

					return alteredRoll;
				});
			}
		}
	};

	Ender.Game.Classes.Feat.prototype.unapply = function(character) {
		if (this.effect.saveBonus) {
			for ( var save in this.effect.saveBonus) {
				character.bonus.save[save] -= this.effect.saveBonus[save];
			}
		}
		if (this.effect.skillBonus) {
			for ( var skill in this.effect.skillBonus) {
				character.bonus.skill[skill] -= this.effect.skillBonus[skill];
			}
		}
		if (this.effect.creatureSize) {
			throw "This feat should never be removed";
		}
		if (this.effect.armorProficiency) {
			throw "This feat should never be removed";
		}
		if (this.effect.shieldProficiency) {
			throw "This feat should never be removed";
		}
		if (this.effect.weaponProficiency) {
			throw "This feat should never be removed";
		}
		if (this.effect.attackBonus) {
			throw "This feat should never be removed";
		}
		if (this.effect.evasionBonus) {
			throw "This feat should never be removed";
		}
		if (this.effect.damageBonus) {
			throw "This feat should never be removed";
		}
	};
}());

(function() {
	Ender.Game.Classes.AccessoryType = function(data) {
		// TODO add accessory type properties
	};
}());

(function() {
	Ender.Game.Classes.ShieldType = function(data) {
		// TODO add shield type properties
	};
}());

(function() {
	Ender.Game.Classes.ArmorType = function(data) {
		// TODO add armor type properties
	};
}());

(function() {
	Ender.Game.Classes.WeaponType = function(id, data) {
		this.id = id;

		if (data.name)
			this.name = data.name;
		else
			this.name = id.capitalize();

		var stringArray = data.damage.split('d');
		this.damage = {
			numberOfDice : parseInt(stringArray[0]),
			sides : parseInt(stringArray[1])
		};
		stringArray = data.crit.split('-');
		this.critRange = {
			lower : parseInt(stringArray[0]),
			higher : parseInt(stringArray[1]) || parseInt(stringArray[0])
		};
	};
}());

(function() {

	/**
	 * Represents a choice made while selecting a character for a New Game.
	 * 
	 * @param data
	 * @param parent -
	 *            the parent
	 * @returns {Ender.Game.Classes.StartingChoice}
	 */
	Ender.Game.Classes.StartingChoice = function(data, parent) {
		this.id = data.id;
		this.name = data.name;
		this.parent = parent;
		this.disabled = data.disabled;
		this.image = "images/portraits/" + data.image;

		if (data.choices) {

			this.categories = [];
			this.characters = [];

			for ( var i = 0; i < data.choices.length; i++) {
				var choice = data.choices[i];
				if (choice.choices) {
					this.categories.push(new Ender.Game.Classes.StartingChoice(choice, this));
				} else {
					this.characters.push(new Ender.Game.Classes.StartingChoice(choice, this));
				}
			}

		}
	};

})();

(function() {

	function generateGrid(height, width) {
		var arenaGrid = [];
		for ( var i = 0; i < height; i++) {
			arenaGrid[i] = [];
			for ( var j = 0; j < width; j++)
				arenaGrid[i][j] = {};
		}
		return arenaGrid;
	}

	function determineNumberOfObstacles(arena, obstacleDensity) {
		var cellNumbers = arena.height * arena.width;
		var obstacleCoefficient = cellNumbers + 1;
		switch (obstacleDensity) {
		case 'sparse':
			obstacleCoefficient = 20;
			break;
		case 'medium':
			obstacleCoefficient = 10;
			break;
		case 'dense':
			obstacleCoefficient = 5;
			break;
		}

		return Math.floor(cellNumbers / obstacleCoefficient);
	}

	function determineReservedCoordinates(playerPosition, enemyPosition, numberOfEnemies) {
		var coordinates = [ playerPosition ];
		if (numberOfEnemies == 1) {
			coordinates.push(enemyPosition[numberOfEnemies]);
		} else {
			coordinates.pushAll(enemyPosition[numberOfEnemies]);
		}
		return coordinates;
	}

	function generateObstaclePositions(arena, numberOfObstacles, reservedCoordinates) {
		var generatedObstaclePositions = [];
		var numberOfGeneratedObstacles = 0;
		obstacleGenerator: while (numberOfGeneratedObstacles < numberOfObstacles) {
			var x = Math.floor(Math.random() * arena.width);
			var y = Math.floor(Math.random() * arena.height);

			for ( var i = 0; i < reservedCoordinates.length; i++) {
				var coordinate = reservedCoordinates[i];
				if (coordinate.x == x && coordinate.y == y) {
					continue obstacleGenerator;
				}
			}

			numberOfGeneratedObstacles++;
			generatedObstaclePositions.push({
				x : x,
				y : y
			});
		}

		return generatedObstaclePositions;
	}

	function generateObstaclesFor(arena, obstacleData, obstacleDensity, numberOfEnemies) {
		var numberOfObstacles = determineNumberOfObstacles(arena, obstacleDensity);
		var reservedCoordinates = determineReservedCoordinates(arena.playerPosition, arena.enemyPosition, numberOfEnemies);

		var generatedObstaclePositions = generateObstaclePositions(arena, numberOfObstacles, reservedCoordinates);

		var obstacles = [];

		var iconArray = obstacleData.icons;
		for ( var i = 0; i < generatedObstaclePositions.length; i++) {
			var icon = iconArray[Math.floor(Math.random() * iconArray.length)];

			var obstacle = {
				position : generatedObstaclePositions[i],
				icon : icon
			};
			obstacles.push(obstacle);
		}

		// TODO apply obstacles to arena
	}

	function generateEnemiesFor(arena, character, numberOfEnemies) {
		var enemies = [];
		for ( var i = 0; i < numberOfEnemies; i++) {
			var enemy = Ender.Game.Repositories.EnemyRepository.getRandomEnemyForCharacter(character);
			enemies.push(enemy);
		}
		arena.placeEnemies(enemies);
	}

	Ender.Game.Classes.ArenaPrototype = function(id, data) {
		this.id = id;
		this.data = data;
	};

	Ender.Game.Classes.ArenaPrototype.prototype.generateArena = function(character, numberOfEnemies, obstacleDensity) {

		var id = this.id;
		var data = this.data;

		var arena = new Ender.Game.Classes.Arena();
		$.extend(arena, {
			prototype : id,
			playerPosition : data.player,
			enemyPosition : data.enemy,
			backgroundImage : "images/terrain/background/" + data.background + ".png",
			height : data.height,
			width : data.width,
		});

		arena.grid = generateGrid(data.height, data.width);

		if (data.obstacles) {
			if (!obstacleDensity)
				obstacleDensity = data.obstacles.defaultDensity;
			if (!obstacleDensity)
				obstacleDensity = "medium";
			if (!numberOfEnemies)
				numberOfEnemies = 1;

			generateObstaclesFor(arena, data.obstacles, obstacleDensity, numberOfEnemies);
		}

		generateEnemiesFor(arena, character, numberOfEnemies);

		return arena;
	};
})();

(function() {
	Ender.Game.Classes.Arena = function() {
		this.prototype = null;
		this.playerPosition = null;
		this.enemyPosition = null;
		this.backgroundImage = null;
		this.height = null;
		this.width = null;
		this.grid = null;
	};

	Ender.Game.Classes.Arena.prototype.placeCharacter = function(character, position, facing) {
		if (!position) {
			position = this.playerPosition;
		}
		if (!facing)
			character.facing = 'right';
		else
			character.facing = facing;
		this.grid[position.y - 1][position.x - 1].character = character;
		character.position = {
			column : position.x - 1,
			row : position.y - 1
		};
	};

	Ender.Game.Classes.Arena.prototype.placeEnemies = function(enemies) {
		if (enemies.length == 1) {
			var enemy = enemies[0];
			var position = this.enemyPosition[1];
			this.placeCharacter(enemy, position, 'left');
		} else {
			var positions = this.enemyPosition[enemies.length];
			for ( var i = 0; i < positions.length; i++) {
				this.placeCharacter(enemies[i], positions[i], 'left');
			}
		}
	};
	
	Ender.Game.Classes.Arena.prototype.getCharacters = function(){
		var characters = [];
		
		for (var i=0; i<this.grid.length; i++){
			for (var j=0; j<this.grid[i].length; j++){
				if (this.grid[i][j].character){
					characters.push(this.grid[i][j].character);
				}
			}
		}
		
		return characters;
	};
	
}());

(function() {
	Ender.Game.Classes.Animation = function(id, data) {
		this.id = id;
		this.imagePath = "images/sprites/units/" + id;
		this.data = data;
	};
	
	var FAKE_ANIMATION_DELAY = 600;

	Ender.Game.Classes.Animation.prototype.renderAnimation = function(data, image){
		var div = $('<div class="animation-frame"></div>');
		div.css({
			'background-image' : 'url(' + this.imagePath + '/' + image + '.png)',
			'width' : data.width + 'px',
			'height' : data.height + 'px',
		});
		return div;
	};
	
	Ender.Game.Classes.Animation.prototype.renderStandingCharacter = function() {
		return this.renderAnimation(this.data.stand, 'stand');
	};
	
	Ender.Game.Classes.Animation.prototype.renderIdleCharacter = function() {
		return this.renderAnimation(this.data.idle, 'idle');
	};

	Ender.Game.Classes.Animation.prototype.hasIdleAnimation = function(){
		return !!this.data.idle;
	};
	
	Ender.Game.Classes.Animation.prototype.hasMoveAnimation = function(){
		return !!this.data.walk;
	};
	
	Ender.Game.Classes.Animation.prototype.hasAttackAnimation = function(){
		return !!this.data.attack;
	};
	
	Ender.Game.Classes.Animation.prototype.stopAnimation = function(){
		if (this.idleAnimationTimeout)
			clearTimeout(this.idleAnimationTimeout);
		if (this.moveAnimationTimeout){
			clearTimeout(this.moveAnimationTimeout);
		}
		if (this.attackAnimationTimeout){
			clearTimeout(this.attackAnimationTimeout);
		}
	};
	

	Ender.Game.Classes.Animation.prototype.startAttackAnimation = function(container, direction, character, callback) {
		var animation = this;
		if (animation.hasAttackAnimation()) {
			// TODO attack animation
			callback();
		} else {
			// fallback attack animation
			animation.attackAnimationTimeout = setTimeout(function() {
				var stand = animation.renderStandingCharacter();
				container.html(stand);

				// determine position
				var position = stand.position();
				var originalPosition = angular.copy(position);
				// correction for left align
				// XXX position.left = position.left.roundTo(50);
				var offset = 25;
				if (direction == 'left') {
					position.left -= offset;
				} else if (direction == 'right') {
					position.left += offset;
				} else if (direction == 'up') {
					position.top -= offset;
				} else if (direction == 'down') {
					position.top += offset;
				}
				
				// determine facing
				if (character.facing == 'left' && direction == 'right') {
					container.removeClass('flipped');
				}
				if (direction == 'left') {
					container.addClass('flipped');
					position.left = -1 * position.left;
				}

				stand.css(position);

				animation.attackAnimationTimeout = setTimeout(function() {
					stand.css(originalPosition);
					callback();
				}, FAKE_ANIMATION_DELAY);
			}, FAKE_ANIMATION_DELAY);
		}
	};
	
	Ender.Game.Classes.Animation.prototype.startMoveAnimation = function(container, path, character, callback){
		var animation = this;
		if (this.hasMoveAnimation()){
			// TODO move animation
			callback();
		} else {
			// fallback move animation
			var stand = this.renderStandingCharacter();
			container.html(stand);
			var counter = 0;
			var previousFacing = character.facing;
			nextFakeMoveFrame();
			
			function nextFakeMoveFrame(){
				var direction = path[counter];

				// determine position
				var position = stand.position();
				// correction for left align
				position.left = position.left.roundTo(50);
				if (direction == 'left') {
					position.left -= 50;
				} else if (direction == 'right') {
					position.left += 50;
				} else if (direction == 'up') {
					position.top -= 50;
				} else if (direction == 'down') {
					position.top += 50;
				}
				
				// determine facing
				if (previousFacing == 'left' && direction == 'right'){
					container.removeClass('flipped');
					previousFacing = direction;
				}
				if (previousFacing== 'left' || direction == 'left'){
					container.addClass('flipped');
					previousFacing = 'left';
					position.left = -1 * position.left;
				}
				
				stand.css(position);
				
				counter++;
				
				if (counter == path.length){
					character.facing = previousFacing;
					// animation ended
					callback();
				}else {
					animation.moveAnimationTimeout = setTimeout(nextFakeMoveFrame, FAKE_ANIMATION_DELAY);
				}
			}
		}
	};
	
	Ender.Game.Classes.Animation.prototype.startIdleAnimation = function(container, callback){
		var animation = this;
		
		var idleDiv = animation.renderIdleCharacter();
		container.html(idleDiv);
		
		var counter = 0;
		animation.idleAnimationTimeout = null;
		nextIdleFrame();
		
		function nextIdleFrame() {

			var positionX = counter * animation.data.idle.width;
			if (positionX > 0) {
				container.find('.animation-frame').css({
					'background-position-x' : '-' + counter * animation.data.idle.width + "px"
				});
			}

			counter++;

			// animation ended
			if (counter > animation.data.idle.frames) {
				container.find('.animation-frame').css({
					'background-position-x' : "0px"
				});
				callback();
				return;
			}

			animation.idleAnimationTimeout = setTimeout(function() {
				nextIdleFrame();
			}, 150);
		}
	};
	
})();

(function() {

	var BASE_EVASION_RATING = 5;

	var CreatureSize = Ender.Game.Enums.CreatureSize;

	var Attributes = Ender.Game.Enums.Attributes;

	var Skills = Ender.Game.Enums.Skills;

	var InventorySlots = Ender.Game.Enums.InventorySlots;

	var ArmorPiercingRatings = {
		'rock' : 0,
		'fist' : 0,
		'bronze' : 1,
		'iron' : 2,
		'steel' : 3
	};

	var ArmorRatings = {
		"cloth" : 0,
		'leather' : 2,
		'bronze' : 3,
		'iron' : 4,
		'steel' : 5
	};

	var Dice = {
		roll : function(numberOfDice, sides) {
			var result = 0;
			for ( var i = 0; i < numberOfDice; i++) {
				var randomnumber = Math.floor(Math.random() * sides) + 1;
				result += randomnumber;
			}
			return result;
		}
	};

	Ender.Game.Classes.Character = function() {
		this.id = undefined; // e.g. "lotr-hobbit"
		this.className = undefined; // e.g. "Hobbit"
		this.universe = undefined; // e.g. "lotr"
		this.inventory = {};
		this.attributes = {};
		this.skills = {};
		this.feats = {};

		this.bonus = {
			save : {
				will : 0,
				fortitude : 0,
				reflex : 0,
				all : 0
			},
			skill : {},
			attack : [],
			damage : [],
			evasion : {
				stackable : {
					dodge : 0
				},
				unstackable : {

				}
			}
		};

		this.proficiencies = {
			weapon : {},
			armor : {},
			shield : {}
		};

		// defaults
		this.size = CreatureSize.medium; // default size
		this.sideArmEquipped = false;
		for ( var skill in Skills) {
			this.skills[skill] = 0;
		}
	};

	Ender.Game.Classes.Character.prototype.rollInitiative = function(){
		return Dice.roll(1, 20) + this.getAttributeBonus("dexterity");
	};
	
	Ender.Game.Classes.Character.prototype.rollSavingThrow = function(save) {
		return Dice.roll(1, 20) + this.bonus.save[save] + this.bonus.save.all;
	};

	Ender.Game.Classes.Character.prototype.computeStats = function() {
		this.computeHealth();
		this.computeSaveBonuses();
	};

	Ender.Game.Classes.Character.prototype.computeSaveBonuses = function() {
		this.bonus.save.will = this.getAttributeBonus("wisdom");
		this.bonus.save.fortitude = this.getAttributeBonus("constitution");
		this.bonus.save.reflex = this.getAttributeBonus("dexterity");
	};

	Ender.Game.Classes.Character.prototype.switchToSidearm = function() {
		this.sideArmEquipped = true;
	};

	Ender.Game.Classes.Character.prototype.switchToMainWeapon = function() {
		this.sideArmEquipped = false;
	};

	Ender.Game.Classes.Character.prototype.attack = function(enemyCharacter, range) {

		var attackRoll = this.makeAttackRoll(enemyCharacter, range);
		console.log('attack roll: ' + attackRoll);

		var evasionRating = enemyCharacter.getEvasionRating(range);
		if (attackRoll < evasionRating)
			return "miss";

		var bodypart = this.makeBodypartRoll();
		console.log('bodypart: ' + bodypart);

		var armorPierceRoll = this.makeArmorPierceRoll(enemyCharacter, range);
		console.log("AP roll: " + armorPierceRoll);

		var armorRating = enemyCharacter.getArmorRating(bodypart);
		console.log('armor rating: ' + armorRating);

		if (armorPierceRoll < armorRating)
			return "absorb";

		var damage = this.makeDamageRoll(enemyCharacter, range);
		var crit = this.makeCriticalRoll(enemyCharacter, range);

		enemyCharacter.applyDamage(damage, bodypart, crit);

		if (crit)
			return damage + " critted";
		else
			return damage + " dealt";
	};

	Ender.Game.Classes.Character.prototype.makeCriticalRoll = function(enemyCharacter, range) {
		var roll = Dice.roll(1, 10);
		return this.getEquippedWeapon().isCritical(roll);
	};

	Ender.Game.Classes.Character.prototype.computeHealth = function() {
		this.health = this.size.health + this.getAttributeBonus(Attributes.constitution);
	};

	Ender.Game.Classes.Character.prototype.makeBodypartRoll = function() {
		var roll = Dice.roll(1, 10);
		switch (roll) {
		case 1:
			return InventorySlots.hands;
		case 2:
			return InventorySlots.feet;
		case 3:
			return InventorySlots.arms;
		case 4:
			return InventorySlots.arms;
		case 5:
			return InventorySlots.legs;
		case 6:
			return InventorySlots.legs;
		case 7:
			return InventorySlots.chest;
		case 8:
			return InventorySlots.chest;
		case 9:
			return InventorySlots.chest;
		case 10:
			return InventorySlots.head;
		default:
			throw "Unreachable case" + roll;
		}
	};

	Ender.Game.Classes.Character.prototype.makeAttackRoll = function(enemyCharacter, range) {
		var roll = Dice.roll(1, 8);
		// attribute bonus
		var attribute = this.getAttributeForAttackBonus(this.getEquippedWeapon(), range);
		roll += this.getAttributeBonus(attribute);
		// size bonus
		roll += this.size.attackBonus;
		for ( var i = 0; i < this.bonus.attack.length; i++) {
			var attackBonusFunction = this.bonus.attack[i];
			roll = attackBonusFunction(this, enemyCharacter, roll, range);
		}
		return roll;
	};

	Ender.Game.Classes.Character.prototype.getAttributeForAttackBonus = function(weapon, range) {
		if (range > 0)
			return "dexterity";
		if (weapon.unarmed)
			return "dexterity";
		// default melee
		return "strength";
	};

	Ender.Game.Classes.Character.prototype.getAttributeBonus = function(attribute) {
		return Math.floor((this.attributes[attribute] - 10) / 2);
	};

	Ender.Game.Classes.Character.prototype.getEvasionRating = function(range) {
		if (range == 0)
			return BASE_EVASION_RATING + this.size.evasionBonus;
		else
			return BASE_EVASION_RATING + this.size.evasionBonus;
	};

	Ender.Game.Classes.Character.prototype.makeArmorPierceRoll = function(enemyCharacter, range) {
		var weapon = this.getEquippedWeapon();
		return ArmorPiercingRatings[weapon.material] + Dice.roll(1, 3);
	};

	Ender.Game.Classes.Character.prototype.getArmorRating = function(bodyPart) {
		if (this.inventory[bodyPart]) {
			return this.inventory[bodyPart].getArmorRating();
		} else {
			// default: plain clothes
			return 0;
		}
	};

	Ender.Game.Classes.Character.prototype.getEquippedWeapon = function() {
		var mainWeapon = this.inventory[InventorySlots.mainHand];
		var sideArm = this.inventory[InventorySlots.sidearm];
		var weapon;
		if (this.sideArmEquipped) {
			weapon = sideArm;
		} else {
			weapon = mainWeapon;
		}
		if (weapon) {
			return weapon;
		} else {
			return Ender.Game.Classes.Weapon.UnarmedWeaponType;
		}
	};

	Ender.Game.Classes.Character.prototype.makeDamageRoll = function(enemyCharacter, range) {
		var weapon = this.getEquippedWeapon();
		var roll = weapon.makeDamageRoll(this);
		for ( var i = 0; i < this.bonus.damage.length; i++) {
			var damageBonusFunction = this.bonus.damage[i];
			roll = damageBonusFunction(this, enemyCharacter, roll, range);
		}
		return roll;
	};

	Ender.Game.Classes.Character.prototype.applyDamage = function(damage, bodypart, crit) {
		if (crit) {
			switch (bodypart) {
			case InventorySlots.head: // instant death
				this.health = 0;
				break;
			case InventorySlots.chest: // double damage
				this.health -= (damage * 2);
				break;
			case InventorySlots.legs: // 1.5x damage
				this.health -= Math.floor(damage * 1.5);
				break;
			case InventorySlots.arms: // 1.5x damage
				this.health -= Math.floor(damage * 1.5);
				break;
			default: // hands & feet
				this.health -= damage;
				break;
			}
		} else {
			this.health -= damage;
		}
	};

	Ender.Game.Classes.Weapon = function(material, damage, critRange) {
		this.material = material;
		this.damage = damage;
		this.critRange = critRange;
	};

	Ender.Game.Classes.Weapon.prototype.isOfType = function(type) {
		// TODO implement weapon.isOfType
		return true;
	};

	Ender.Game.Classes.Weapon.prototype.getDamageString = function() {
		return this.damage.numberOfDice + 'd' + this.damage.sides;
	};

	Ender.Game.Classes.Weapon.prototype.getCritRangeString = function() {
		if (this.critRange.lower === this.critRange.higher) {
			return this.critRange.higher;
		} else {
			return this.critRange.lower + '-' + this.critRange.higher;
		}
	};

	Ender.Game.Classes.UnarmedAttack = function() {
		this.unarmed = true;
	};

	Ender.Game.Classes.UnarmedAttack.prototype = new Ender.Game.Classes.Weapon('fist', null, '10');

	Ender.Game.Classes.UnarmedAttack.prototype.makeDamageRoll = function(character) {
		var strengthBonus = character.getAttributeBonus(Attributes.strength);
		return Math.max(strengthBonus, 1);
	};

	Ender.Game.Classes.Weapon.UnarmedWeaponType = new Ender.Game.Classes.UnarmedAttack();

	Ender.Game.Classes.Weapon.prototype.makeDamageRoll = function(character) {
		return Dice.roll(this.damage.numberOfDice, this.damage.sides);
	};

	Ender.Game.Classes.Weapon.prototype.isCritical = function(roll) {
		return roll >= this.critRange.lower && roll <= this.critRange.higher;
	};

	Ender.Game.Classes.Armor = function(material, partial) {
		this.material = material;
		if (partial) {
			this.partial = true;
		}
	};

	Ender.Game.Classes.Armor.prototype.getArmorRating = function() {
		if (this.partial)
			return Math.floor(ArmorRatings[this.material] / 2);
		else
			return ArmorRatings[this.material];
	};

	Ender.Game.Classes.Shield = function() {

	};

	Ender.Game.Classes.Accessory = function() {

	};

})();

(function() {
	Ender.Game.Classes.Enemy = function() {

	};

	Ender.Game.Classes.Enemy.prototype = new Ender.Game.Classes.Character();
	
	Ender.Game.Classes.Enemy.prototype.takeAction = function(arena, actionFinished){
		// TODO implement AI
		actionFinished();
	};

}());

(function() {
	Ender.Game.Factories.createCharacterFromStartingChoice = function(startingChoice) {
		var character = new Ender.Game.Classes.Character();

		// create inventory
		for ( var slot in startingChoice.inventory) {
			character.inventory[slot] = Ender.Game.Repositories.ItemRepository[startingChoice.inventory[slot]];
		}

		return character;
	};
})();